//Copyright (C) 2000-2006 Gnanasekaran Swaminathan <sekar@sekar.net>
//
//Permission is granted to use at your own risk and distribute this software
//in source and  binary forms provided  the above copyright notice and  this
//paragraph are  preserved on all copies.  This software is provided "as is"
//with no express or implied warranty.

#ifndef SOCK_H
#define SOCK_H

#include <string>
#include <winsock2.h>
#include "socklib.h"

namespace socklib {
  extern std::string   ipaddr2string(unsigned long ipaddr);
  extern const char*   ipaddr2str(unsigned long ipaddr);
  extern bool          is_valid_ipaddr(const char* ipaddrstr);

  struct inetaddr: public sockaddr_in {
    inetaddr();
    inetaddr(unsigned short port); // port in host byte order
    inetaddr(const char* host, unsigned short port); // port in host byte order
    inetaddr(unsigned long addr, unsigned short port); // addr is in network byte order but port is in host byte order
    inetaddr(const struct sockaddr_in& addr);

    bool         operator==(const inetaddr& b) const;
    bool         operator<(const inetaddr& b) const;
    void         set_local_addr();

    std::string  get_host_name() const;
    std::string  get_addr_string(bool shortform=false) const; // return ipaddr:port if shortform is false; otherwise return ipaddr
  };

  struct inetsocket: connection {
  private:
    struct sockdata {
      WSADATA wsadata;

      sockdata();
      ~sockdata();
    };

    static sockdata onlyone;

  public:
    SOCKET          sock;

    inetsocket(SOCKET s=INVALID_SOCKET): sock(s) {}
    inetsocket(int socktype, int protocol);
    virtual ~inetsocket();
    virtual void   write(const void* buf, size_t len) { send(buf, (int)len, 0); }
    virtual size_t read(void* buf, size_t maxlen) { return recv(buf, (int)maxlen, 0); }

    void open(int socktype, int protocol=IPPROTO_IP);
    void close();

    // port is in host byte order
    void bind(unsigned short port);
    void bind(const char* host, unsigned short port);
    void bind(unsigned long addr, unsigned short port); //address is in net byte order and port in host byte order
    void bind(const struct sockaddr_in& addr);
    void connect(const char* host, unsigned short port);
    void connect(unsigned long addr, unsigned short port);//address is in net byte order and port in host byte order
    void connect(const struct sockaddr_in& addr);
    void getpeername(struct sockaddr_in& addr);
    void getsockname(struct sockaddr_in& addr);
    std::string getlocalhost();
    void listen(int backlog=0);
    SOCKET accept(struct sockaddr_in& addr);
    void shutdown(int how);

    // for recv and send functions the caller must handle error
    int  recv(void* buf, int buflen, int flags=0);
    int  recvfrom(struct sockaddr_in& addr,
                  void* buf, int buflen, int flags=0);
    int  recv_nbio(void* buf, int buflen, int flags=0); // ioctl_nbio(true) must be called before you can use this function. returns SOCKET_ERROR if socket would block
    void send(const void* buf, int buflen, int flags=0);
    void sendto(const struct sockaddr_in& addr,
                const void* buf, int buflen, int flags=0);

    bool is_readready(const struct timeval* tv=0);
    bool is_writeready(const struct timeval* tv=0);
    bool is_exceptready(const struct timeval* tv=0);
    bool is_readready(int secs, int usecs=0);
    bool is_writeready(int secs, int usecs=0);
    bool is_exceptready(int secs, int usecs=0);

    void          ioctl_nbio(bool set); // set non-blocking io
    unsigned long ioctl_nread(); // return number of bytes available for single recv
    bool          ioctl_atmark(); // return true if out of band data has not been read
    void          ioctl(int cmd, unsigned long& cmdarg);

    bool          getopt_acceptconn(); // is sock listening?
    bool          getopt_broadcast(); // is sock capable of broadcasting messages?
    bool          getopt_debug(); // is debuging enabled?
    bool          getopt_dontlinger(); // is so_linger option disabled?
    bool          getopt_dontroute(); // is routing disabled?
    int           getopt_error(); // return current socket error and reset the error
    bool          getopt_keepalive(); // are keepalives being sent?
    struct linger getopt_linger(); // get current linger
    unsigned int  getopt_maxmsgsize(); // get current maximum datagram size
    bool          getopt_oobinline(); // return true if out-of-band data is being received in normal data stream
    WSAPROTOCOL_INFO getopt_protocolinfo(); // return protocol info of the WinSock service provider
    int           getopt_rcvbuf(); // buffer size for recvs
    bool          getopt_reuseaddr(); // return true if socket can be bound to an existing address that is in use
    int           getopt_sndbuf(); // buffer size for sends
    int           getopt_type(); // socket type like SOCKET_DGRAM, SOCKET_STREAM
    bool          getopt_tcpnodelay(); // return true if Nagle algorithm is disabled
    void          getsockopt(int level, int optname, void* optval, int& optlen);

    void setopt_broadcast(bool set); // allow transmission of broadcast messages on the socket
    void setopt_debug(bool set); // record debugging information
    void setopt_dontlinger(bool set); // set linger off
    void setopt_dontroute(bool set); // set routing off and send messages directly to interface
    void setopt_keepalive(bool set); // send keep alives
    void setopt_linger(const struct linger& lg);
    void setopt_linger(unsigned short onoff, unsigned short timeout);
    void setopt_oobinline(bool set); 
    void setopt_rcvbuf(int size);
    void setopt_reuseaddr(bool set);
    void setopt_sndbuf(int size);
    void setopt_tcpnodelay(bool set); // turn off nagle algorithm
    void setopt_rcvtimeo(int milliseconds);
    void setopt_sndtimeo(int milliseconds);
    void setsockopt(int level, int optname, const void* optval, int optlen);
  };
}

#endif // SOCK_H
